{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pybroker\n",
    "from pybroker import Strategy, StrategyConfig\n",
    "from pybroker.ext.data import AKShare"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def buy_highest_volume(ctx):\n",
    "    # If there are no long positions across all tickers being traded:\n",
    "    if not tuple(ctx.long_positions()):\n",
    "        ctx.buy_shares = ctx.calc_target_shares(1)\n",
    "        ctx.hold_bars = 2\n",
    "        ctx.score = ctx.volume[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = StrategyConfig(max_long_positions=1)\n",
    "strategy = Strategy(AKShare(), '6/1/2021', '6/1/2024', config)\n",
    "strategy.add_execution(buy_highest_volume, ['000001', '000002', '600001', '600158'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def pos_size_handler(ctx):\n",
    "    # Fetch all buy signals.\n",
    "    signals = tuple(ctx.signals(\"buy\"))\n",
    "    # Return if there are no buy signals (i.e. there are only sell signals).\n",
    "    if not signals:\n",
    "        return\n",
    "    # Calculates the inverse volatility, where volatility is defined as the\n",
    "    # standard deviation of close prices for the last 100 days.\n",
    "    get_inverse_volatility = lambda signal: 1 / np.std(signal.bar_data.close[-100:])\n",
    "    # Sums the inverse volatilities for all of the buy signals.\n",
    "    total_inverse_volatility = sum(map(get_inverse_volatility, signals))\n",
    "    for signal in signals:\n",
    "        size = get_inverse_volatility(signal) / total_inverse_volatility\n",
    "        # Calculate the number of shares given the latest close price.\n",
    "        shares = ctx.calc_target_shares(size, signal.bar_data.close[-1], cash=95_000)\n",
    "        ctx.set_shares(signal, shares)\n",
    "\n",
    "strategy.set_pos_size_handler(pos_size_handler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result = strategy.backtest()\n",
    "result.trades"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result.metrics_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result.portfolio"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Abroker",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
